package cn.dengta.webapp.base.util;

import java.util.Date;
import java.util.Objects;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import cn.dengta.common.model.TimeInterval;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import me.codeplayer.util.*;
import org.apache.ibatis.jdbc.SQL;

import static cn.dengta.webapp.base.entity.TimeBasedEntity.ADD_TIME;

/**
 * 工具方法类
 */
public abstract class MybatisUtil {

	public static void appendSqlInterval(@Nonnull SQL sb, @Nonnull String column, TimeInterval interval, String prefix) {
		if (StringUtil.notEmpty(column) && interval != null) {
			prefix = X.expectNotNull(prefix, "interval");
			Date begin = interval.getFinalBegin();
			if (begin != null) {
				sb.WHERE(column + " >= #{" + prefix + ".finalBegin}");
			}
			Date end = interval.getFinalEnd();
			if (end != null) {
				sb.WHERE(column + " <= #{" + prefix + ".finalEnd}");
			}
		}
	}

	public static void appendSqlIntervalConflict(@Nonnull SQL sb, @Nonnull String addTime, @Nonnull String startColumn, @Nonnull String endColumn, TimeInterval interval, @Nonnull String prefix, @Nonnull String rangePrefix, boolean endTimeExistNull) {
		if (StringUtil.notEmpty(startColumn) && interval != null && interval.getBegin() != null) {
			sb.WHERE(addTime + " >= #{" + rangePrefix + ".finalBegin} AND " + addTime + " <= #{" + rangePrefix + ".finalEnd} ");
			sb.WHERE(endTimeExistNull ? "(!(" + endColumn + " <= #{" + prefix + ".begin} OR " + startColumn + " >= #{" + prefix + ".end}) OR (" + startColumn + " >= #{" + rangePrefix + ".finalBegin} AND " + endColumn + " IS NULL))" :
					"(!(" + endColumn + " <= #{" + prefix + ".begin} OR " + startColumn + " >= #{" + prefix + ".end}))");
		}
	}

	public static void appendSqlIntervalConflict(@Nonnull SQL sb, @Nonnull String startColumn, @Nonnull String endColumn, TimeInterval interval, @Nonnull String prefix, boolean finalInterval) {
		if (StringUtil.notEmpty(startColumn) && interval != null && interval.getBegin() != null) {
			sb.WHERE("(!(" + endColumn + " <= #{" + prefix + (finalInterval ? ".finalBegin} OR " : ".begin} OR ") + startColumn + " >= #{" + prefix + (finalInterval ? ".finalEnd}))" : ".end}))"));
		}
	}

	public static <T> void appendEqSql(@Nonnull SQL sql, @Nonnull String column, @Nullable T obj, String value) {
		appendSql(sql, column, SqlKeyword.EQ, obj, Objects::nonNull, value);
	}

	public static <T> void appendSql(@Nonnull SQL sql, @Nonnull String column, @Nonnull SqlKeyword key, @Nullable T obj, Predicate<T> filter, String value) {
		X.use(appendSql(column, key, obj, filter, value), sql::WHERE);
	}

	public static <T> void appendSql(@Nonnull SQL sql, @Nonnull String column, @Nonnull SqlKeyword key, @Nullable T obj, String value) {
		X.use(appendSql(column, key, obj, Objects::nonNull, value), sql::WHERE);
	}

	public static void appendInSql(@Nonnull SQL sql, @Nonnull String column, Object array, boolean isInclude, boolean isString) {
		if (X.isValid(array)) {
			sql.WHERE(ArrayUtil.getInSQL(new StringBuilder(column), array, isInclude, isString).toString());
		}
	}

	public static void appendInIdSql(@Nonnull SQL sql, @Nonnull String column, Object array) {
		appendInSql(sql, column, array, true, false);
	}

	public static String inIdSql(@Nonnull String column, Object array) {
		if (X.isValid(array)) {
			return ArrayUtil.getInSQL(new StringBuilder(column), array, true, false).toString();
		}
		return null;
	}

	public static String inStrSql(@Nonnull String column, Object array) {
		if (X.isValid(array)) {
			return ArrayUtil.getInSQL(new StringBuilder(column), array, true, true).toString();
		}
		return null;
	}

	public static void appendInStrSql(@Nonnull SQL sql, @Nonnull String column, Object array) {
		appendInSql(sql, column, array, true, true);
	}

	public static <T> String appendSql(@Nonnull String column, @Nonnull SqlKeyword key, @Nullable T obj, Predicate<T> filter, String value) {
		String sql = null;
		if (obj != null && filter.test(obj)) {
			switch (key) {
				case EQ:
				case NE:
				case GT:
				case GE:
				case LT:
				case LE:
					sql = column + " " + key.getSqlSegment() + X.expectNotNull(value, obj);
					break;
				case LIKE:
				case NOT_LIKE:
					sql = column + " " + key.getSqlSegment() + " CONCAT('%', '" + StringUtil.escapeSQLLike(X.expectNotNull(value, StringUtil.toString(obj))) + "', '%')";
					break;
				case IN:
				case NOT_IN:
					sql = column + " " + key.getSqlSegment() + " ('" + X.expectNotNull(value, obj) + "') ";
					break;
				case IS_NOT_NULL:
					sql = column + " " + key.getSqlSegment();
					break;
			}
		}
		return sql;
	}

	public static <T> void wrapperInterval(@Nonnull QueryWrapper<T> wrapper, @Nullable String column, @Nullable TimeInterval interval) {
		if (interval != null) {
			column = StringUtil.isEmpty(column) ? ADD_TIME : column;
			Date finalBegin = interval.getFinalBegin();
			wrapper.ge(finalBegin != null, column, finalBegin);
			Date finalEnd = interval.getFinalEnd();
			wrapper.le(finalEnd != null, column, finalEnd);
		}
	}

	public static <T> void wrapperInOrEq(@Nonnull QueryWrapper<T> wrapper, @Nullable String column, @Nullable Object[] types) {
		if (types != null) {
			if (X.size(types) == 1) {
				wrapper.eq(X.isValid(types[0]), column, types[0]);
				return;
			}
			wrapper.in(X.isValid(types), column, types);
		}
	}

	public static <T> void wrapperInterval(@Nonnull LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, @Nullable TimeInterval interval) {
		if (interval != null) {
			Date finalBegin = interval.getFinalBegin();
			wrapper.ge(finalBegin != null, column, finalBegin);
			Date finalEnd = interval.getFinalEnd();
			wrapper.le(finalEnd != null, column, finalEnd);
		}
	}

	/**
	 * 为不支持毫秒级的低精度 MySQL 日期数据类型提供时间范围的查询条件封装
	 */
	public static <T> void wrapperIntervalLowScale(@Nonnull QueryWrapper<T> wrapper, @Nullable String column, @Nullable TimeInterval interval) {
		if (interval != null) {
			column = StringUtil.isEmpty(column) ? ADD_TIME : column;
			Date finalBegin = interval.getFinalBegin();
			wrapper.ge(finalBegin != null, column, finalBegin);
			Date end = interval.getEnd();
			if (end != null) {
				final int endCalendarField = interval.getEndCalendarField();
				if (endCalendarField != -1) {
					end = interval.getEasyDate(end).endOf(endCalendarField).setMillisecond(0).toDate();
				} else {
					final long time = end.getTime(), reminder = time % 1000L;
					if (reminder > 0) {
						end = new Date(time - reminder);
					}
				}
				wrapper.le(true, column, end);
			}
		}
	}

}
